"""应用重部署"""

import os
from src.core import log
from src.core import Enums
from src.mod import Search
from src.db import dbm, dbs
from src.mod import safe_remove
from src.core.variables.status import *
from src.utils.deep import get_os_info
from .backup_ import Java as Java_backup
from src.core.variables.constant import *
from src.core.variables.exception import *
from .backup_ import Static as Static_backup
from src.core.variables.temp import MIDDLEWARE
from src.mod.file_exchanger.distribute import Distribute
from src.apps.hosting.stater import Jar_manager, Static_manager
from src.core.variables.cls import HOSTING_JAVA_RUNNER_DICT, HOSTING_STATIC_RUNNER_DICT
from sqlalchemy import update


# TODO 下个版本更新
def _anytime_restore(progress, stype, back_id, schema):
    """随时还原，重部署失败时，自动还原"""

    def cost(func):
        def wrapper(*args, **kwargs):
            nonlocal progress, back_id, schema
            try:
                return func(*args, **kwargs)
            except Exception as e:
                log.ERROR(e)
                match stype:
                    # Java部署失败，还原
                    case Enums.Hosting.Stypes.JAVA:
                        log.INFO("部署失败，正在还原...")
                        with dbm.get("hosting_Java") as db_session:
                            from sqlalchemy.orm import scoped_session
                            db_session: scoped_session
                            back_app = (
                                db_session.query(dbs.hosting_Java.App_backup)
                                .filter(dbs.hosting_Java.App_backup.back_id == back_id)
                                .first()
                            )
                            restore_app = back_app.__dict__
                            restore_app.pop("_sa_instance_state", None)
                            restore_app.pop("back_id", None)
                            if progress < 1:
                                log.ERROR("还原失败，请手动处理！")
                                return ERROR_CODE_CUSTOMIZE(
                                    f"部署失败，操作无法撤回，请手动处理！"
                                )
                            log.INFO("数据库还原...")
                            db_session.query(dbs.hosting_Java.App_backup).filter(
                                dbs.hosting_Java.App_backup.back_id == back_id
                            ).delete()
                            if progress < 2:
                                return ERROR_CODE_CUSTOMIZE("部署失败，操作已撤回！")
                            log.INFO("仓库新版本删除...")
                            safe_remove(
                                f"{CONF.PATH.REPO}/{schema.app_id}/{schema.time_dir}"
                            )
                            if progress == 3:
                                log.INFO("应用启动中...")
                                runner = HOSTING_JAVA_RUNNER_DICT[schema.app_id]
                                runner.start()
                                return ERROR_CODE_CUSTOMIZE("部署失败，操作已撤回！")
                            if progress >= 4:
                                log.INFO("还原back数据...")
                                db_session.query(dbs.hosting_Java.App).filter(
                                    dbs.hosting_Java.App.app_id == schema.app_id
                                ).update(restore_app)
                                if not dbm.commit(db_session):
                                    log.ERROR("数据库还原失败！")
                                    return ERROR_CODE_CUSTOMIZE(
                                        "部署失败，操作已撤回！"
                                    )
                                log.INFO("数据库还原完成...")
                                log.INFO("重新分发旧版本到运行池...")
                                db_session.query(dbs.hosting_Java.App).filter(
                                    dbs.hosting_Java.App.app_id == schema.app_id
                                ).update(
                                    {
                                        "run_path": Distribute.repo_to_run(
                                            schema.app_id,
                                            schema.node,
                                            schema.time_dir,
                                        )
                                    }
                                )
                                if not dbm.commit(db_session):
                                    log.ERROR("数据库还原失败！")
                                    return ERROR_CODE_CUSTOMIZE(
                                        "部署失败，操作已撤回！"
                                    )
                                log.INFO("重新生成runner...")
                                runner = Jar_manager(schema.app_id)
                                HOSTING_JAVA_RUNNER_DICT[schema.app_id] = runner
                                log.INFO("应用启动中...")
                                runner.start()
                                log.INFO("已经还原到部署前！")
                            return ERROR_CODE_CUSTOMIZE("部署失败，操作已撤回！")
                    case _:
                        log.ERROR(f"未知类型: {stype.value}")
                        return ERROR_CODE_CUSTOMIZE(
                            "部署失败，当前类型不支持暂还原，请手动处理！"
                        )

        return wrapper

    return cost


class __Java:
    # 进度：
    #   0. 无进度
    #   1. 备份当前版本
    #   2. 载入仓库
    #   3. 停止应用
    #   4. 分发到运行池
    #   5. 重新生成runner
    progress = 0
    back_id = None
    schema = None
    stype = Enums.Hosting.Stypes.JAVA
    from src.schema.hosting import deploy_java_schema
    # @_anytime_restore(progress, stype, back_id, schema)
    def execute(self, schema: deploy_java_schema):
        """重新部署java"""
        # 执行备份操作，支持链式调用
        self.back_id = Java_backup(schema.app_id).current_data().back_id
        self.old_app = Search.Hosting.app_by_id(schema.app_id)
        self.progress += 1
        # 将应用拉入仓库
        alias = Search.Hosting.app_by_id(schema.app_id).alias
        schema.save_path = f'{CONF.PATH.REPO}/{alias}/{self.old_app.time_dir}/{self.old_app.file_name}' if schema.save_path == '0' else schema.save_path
        schema.time_dir = get_os_info().local_time("continuous") if schema.time_dir == '0' else schema.time_dir
        Distribute.temp_to_repo(schema.save_path, schema.node, schema.time_dir, alias)
        self.progress += 1
        with dbm.get("hosting_Java") as db_session:
            from sqlalchemy.orm import scoped_session
            db_session: scoped_session
            runner = HOSTING_JAVA_RUNNER_DICT.get(schema.app_id, None)
            # 如果 runner 存在，说明应用运行，则先停止应用
            runner.stop() if runner is not None else ...
            runner.listen_status = False
            self.progress += 1
            db_session.execute(
                update(dbs.hosting_Java.App)
                .where(dbs.hosting_Java.App.app_id == schema.app_id)
                .values(
                    file_name=os.path.basename(schema.save_path),
                    deploy_time=schema.time_dir,
                    status="dead",
                    port=schema.port,
                    inpara=schema.inpara,
                    notes=schema.notes,
                    version=schema.version,
                    source="deploy",
                    time_dir=schema.time_dir,
                    before_id=self.back_id,
                )
            )

            # 提交数据库更改
            if dbm.commit(db_session):
                log.INFO(f"应用 {schema.app_id} 数据库记录已更新")
                # 此处不需要更新进度，因为当数据库提交失败后，会自动还原
            else:
                raise MAIN_EXCEPTION(f"提交数据库更改失败！")
            # 使用分发器将文件投入运行池
            run_path = Distribute.repo_to_run(
                schema.app_id, schema.node, schema.time_dir
            )
            self.progress += 1
            # 更新数据库记录
            db_session.execute(
                update(dbs.hosting_Java.App)
                .where(dbs.hosting_Java.App.app_id == schema.app_id)
                .values(run_path=run_path)
            )
            # 提交数据库更改
            if dbm.commit(db_session):
                log.INFO(f"应用 {schema.app_id} 数据库记录已更新")
                # 此处不需要更新进度，因为当数据库提交失败后，会自动还原
            else:
                raise MAIN_EXCEPTION(f"提交数据库更改失败！")
            # 替换运行池中的应用

            runner = Jar_manager(schema.app_id)
            runner.start()
            HOSTING_JAVA_RUNNER_DICT[schema.app_id] = runner
            self.progress += 1
            return SUCCESS_CODE_CUSTOMIZE("应用发布成功！", 200)


java = __Java().execute



class __Static:
    # 进度：
    #   0. 无进度
    #   1. 备份当前版本
    #   2. 载入仓库
    #   3. 停止应用
    #   4. 分发到运行池
    #   5. 重新生成runner
    progress = 0
    back_id = None
    schema = None
    stype = Enums.Hosting.Stypes.STATIC
    from src.schema.hosting import deploy_static_schema

    # @_anytime_restore(progress, stype, back_id, schema)
    def execute(self, schema: deploy_static_schema):
        """重新部署static"""

        # 执行备份操作，支持链式调用
        self.back_id = Static_backup(schema.app_id).current_data().back_id
        self.old_app = Search.Hosting.app_by_id(schema.app_id)
        self.progress += 1
        # 将应用拉入仓库
        alias = Search.Hosting.app_by_id(schema.app_id).alias
        schema.save_path = f'{CONF.PATH.REPO}/{alias}/{self.old_app.time_dir}/{self.old_app.file_name}' if schema.save_path == '0' else schema.save_path
        schema.time_dir = get_os_info().local_time("continuous") if schema.time_dir == '0' else schema.time_dir
        Distribute.temp_to_repo(schema.save_path, schema.node, schema.time_dir, alias)
        self.progress += 1
        with dbm.get("hosting_Static") as db_session:
            runner = HOSTING_STATIC_RUNNER_DICT.get(schema.app_id, None)
            # 如果 runner 存在，说明应用运行，则先停止应用
            runner.disable() if runner is not None else ...
            runner.listen_status = False
            self.progress += 1
            db_session.execute(
                update(dbs.hosting_Static.App)
                .where(dbs.hosting_Static.App.app_id == schema.app_id)
                .values(
                    file_name=os.path.basename(schema.save_path),
                    deploy_time=schema.time_dir,
                    status="unmapped",
                    notes=schema.notes,
                    version=schema.version,
                    source="deploy",
                    time_dir=schema.time_dir,
                    before_id=self.back_id,
                    nginx_conf=schema.nginx_conf,
                )
            )
            # 提交数据库更改
            if dbm.commit(db_session):
                log.INFO(f"应用 {schema.app_id} 数据库记录已更新")
                # 此处不需要更新进度，因为当数据库提交失败后，会自动还原
            else:
                raise MAIN_EXCEPTION(f"提交数据库更改失败！")
        runner = Static_manager(schema.app_id)
        runner.enable()
        HOSTING_STATIC_RUNNER_DICT[schema.app_id] = runner
        self.progress += 1
        return SUCCESS_CODE_CUSTOMIZE("应用发布成功！", 200)
    
static = __Static().execute